
package app;

/**
 * Klasa abstrakcyjna określająca zachowanie i właściwości każdego obiektu, który
 * może się poruszać po trasie.
 * @author morohusky
 */
public abstract class Sprite {
    private final Key _up;
    private final Key _down;
    private final Key _left;
    private final Key _right;
    private SpriteStats _stats;
    protected double _speed = 0d;
    protected double _angle = 0d;
    protected double _posX = 100d;
    protected double _posY = 100d;
    
    
    /**
     * Konstruktor parametryczny
     * @param stats Właściwości obiektu
     * @param controls Klawisze do sterowania obiektem
     */
    public Sprite(SpriteStats stats, Key[] controls) {
        _stats = stats;
        _up = controls[0];
        _down = controls[1];
        _left = controls[2];
        _right = controls[3];
    }
    
    
    /**
     * Obraca obiekt
     * @param angle Kąt o jaki trzeba obrócić obiekt
     */
    protected abstract void rotate(double angle);
    
    /**
     * "Getter" kąta obrócenia obiektu
     * @return Kąt
     */
    public double getAngle() {
        return _angle;
    }
    
    /**
     * "Getter" prędkości poruszania się obiektu
     * @return Prędkość
     */
    public double getSpeed() {
        return _speed;
    }
    
    /**
     * Przywrócenie obiektu w stan początkowy
     */
    public void reset() {
        _angle = 0d;
        rotate(_angle);
        _speed = 1d;
    }
    
    /**
     * Ustawienie nowych statystyk obiektu
     * @param stats Nowe statystyki
     */
    public void setStats(SpriteStats stats) {
        _stats = stats;
    }
    
    /**
     * Poruszenie obiektu z uwzględnieniem ilości klatek na sekundę
     * @param fps Ilość klatek na sekundę
     */
    public void move(double fps) {
        steer(fps);
        accelerate(fps);
        
        double radians = Math.toRadians(_angle);
        double ax = Math.sin(radians) * _speed;
        double ay = Math.cos(radians) * _speed;
        _posX += ax;
        _posY -= ay;
    }
    
    /**
     * Obrócenie obiektu z uwzględnieniem opóźnien w wyświetlaniu
     * @param delta Opóźnienia w wyświetlaniu
     */
    private void steer(double delta) {
        if (Math.abs(_speed) > 0.001d) {
            if (_left.havePower()) {
                double change = _stats.getRotation() * Math.signum(_speed);
                
                _angle -= change * _left.getPowerPercentage() * delta;
                
                if (_angle < -180d) {
                    _angle = 360d + _angle;
                }
                else if (_angle > 180d) {
                    _angle = _angle - 360d;
                }
                
                rotate(_angle);
            }
            if (_right.havePower()) {
                double change = _stats.getRotation() * Math.signum(_speed);
                
                _angle += change * _right.getPowerPercentage() * delta;
                
                if (_angle < -180d) {
                    _angle = 360d + _angle;
                }
                else if (_angle > 180d) {
                    _angle = _angle - 360d;
                }
                
                rotate(_angle);
            }
        }
    }
    
    /**
     * Poruszenie obiektu z uwzględnieniem opóźnienia w wyświetlaniu
     * @param delta Opóźnienie w wyświetlaniu
     */
    private void accelerate(double delta) {
        if (!_up.havePower() && !_down.havePower()) {
            if (_speed > 0d) {
                _speed -= delta * _stats.getFreeSlowDown();
            }
            else if (_speed < 0d) {
                _speed += delta * _stats.getFreeSlowDown();
            }
            
            if (Math.abs(_speed) <= delta * _stats.getFreeSlowDown()) {
                _speed = 0d;
            }
        }
        else {
            if (_up.havePower()) {
                if (_speed > 0d) {
                    _speed += delta * _stats.getAcceleration() * _up.getPowerPercentage();

                    double topSpeed = delta * _stats.getTopSpeed();
                    if (_speed > topSpeed) {
                        _speed = topSpeed;
                    }
                }
                else {
                    _speed += delta * _stats.getBreaks() * _up.getPowerPercentage();
                }
                
                //_speed *= _up.getPower() * 0.01;
            }
            if (_down.havePower()) {
                if (_speed < 0d) {
                    _speed -= delta * _stats.getAcceleration() * _down.getPowerPercentage();

                    double maxReverse = delta * _stats.getMaxReverseSpeed();
                    if (_speed < maxReverse) {
                        _speed = maxReverse;
                    }
                }
                else {
                    _speed -= delta * _stats.getBreaks() * _down.getPowerPercentage();
                }
                
                //_speed *= _down.getPower() * 0.01;
            }
        }
    }
    
}
